﻿// *********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
// *********************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using E = Microsoft.Research.DkalEngine;
using System.IO;
using Microsoft.Research.DkalEngine;
using System.Configuration;

namespace DkalController
{
    /// <summary>
    /// Host App for Dkal Engine
    /// </summary>
    public class MessageController : ICommunicator
    {
        #region Variables

        public delegate void InfonProcessedHandler(object sender, DkalInfoEventArgs e);
        public delegate void ProcessedMessageHandler(object sender, string e);
        public event InfonProcessedHandler OnInfonProcessed;
        public event ProcessedMessageHandler OnRequestProcessed;
        private E.Engine e;
        private ParsingCtx pctx;
        List<E.Ast.Assertion> decls;

        #endregion

        #region Properties


        /// <summary>
        /// returns Engine Instance
        /// </summary>
        public E.Engine EngineInstance
        {
            get
            {
                if (e == null)
                    throw new Exception("Engine object not initialized yet");

                return e;
            }
        }

        /// <summary>
        /// returns Assertion List
        /// </summary>
        public List<E.Ast.Assertion> AssertionsList
        {
            get
            {
                if (decls == null)
                    throw new Exception("Assertions list not initialized yet");
                return decls;
            }
        }
        #endregion

        #region Constructor
        public MessageController(string contextPath)
        {
            if (String.IsNullOrEmpty(contextPath))
                throw new Exception("Please specify a dkal context");

            this.ContextPath = contextPath;
            try
            {
                InitializeEngines();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region Methods
        void InitializeEngines()
        {
            InitEngine();
        }

        public string ContextPath { get; set; }

        public ParsingCtx ParsingContext
        {
            get { return pctx; }
        }

        public Ast.Principal GetPrincipal(string principalName)
        {
            return pctx.LookupOrAddPrincipal(principalName);
        }

        void InitEngine()
        {
            // parsing the policy file
            decls = new List<E.Ast.Assertion>();
            pctx = null;
            pctx = new E.ParsingCtx();
            string me = null;

            try
            {
                foreach (var a in pctx.ParsePrelude())
                    decls.Add(a);

                System.IO.TextReader readFile = null;
                try
                {
                    readFile = new StreamReader(this.ContextPath);

                    // the “from-database.dkal” will be used in error messages
                    foreach (var a in pctx.ParseStream("from-database.dkal", readFile))
                        decls.Add(a);

                    readFile.Close();
                    readFile = null;
                }
                catch (IOException ex)
                {
                    throw new Exception(ex.Message);
                }

                var opts = E.Options.Create();

                bool _isSqlServer = true;

                if (_isSqlServer)
                {
                    if (String.IsNullOrEmpty(ConfigurationManager.AppSettings["SqlServerSubstrateConnectionString"]))
                        throw new Exception("SqlServerSubstrateConnectionString is empty");

                    opts.PrivateSql = ConfigurationManager.AppSettings["SqlServerSubstrateConnectionString"];
                    opts.Trace = 0;

                    e = E.Engine.Config(opts);
                    e.Reset();  // this starts a thread for the particular engine
                }
                else
                {
                    if (String.IsNullOrEmpty(ConfigurationManager.AppSettings["SqlCESubstrateConnectionString"]))
                        throw new Exception("SqlServerSubstrateConnectionString is empty");

                    string conString = ConfigurationManager.AppSettings["SqlCESubstrateConnectionString"];

                    if (string.IsNullOrEmpty(conString))
                        throw new Exception("SqlCESubstrateConnectionString is null");
                    opts.PrivateSql = conString;
                    opts.Trace = 0;

                    e = E.Engine.Config(opts);
                    e.Reset(Util.DataSourceType.SqlCe);
                }
                foreach (var a in decls)
                    e.AddAssertion(a);
                e.AddDefaultFilter();
            }
            catch (E.Util.SyntaxError se)
            {
                throw se;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Sends an infon to the current Engine instance
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="principal"></param>
        public void SendMessage(string msg, string principal)
        {
            Ast.Term termObj = pctx.ParseInfon(msg);
            e.AddInfon(termObj);
            e.Talk(this);
        }
        #endregion

        #region ICommunicator Members

        public void ExceptionHandler(Exception value)
        {
            throw new NotImplementedException();
        }

        public void Knows(Ast.Knows value)
        {
            throw new NotImplementedException();
        }

        public Ast.Principal PrincipalById(int value)
        {
            throw new NotImplementedException();
        }

        public int PrincipalId(Ast.Principal value)
        {
            throw new NotImplementedException();
        }

        public void QueryResults(Ast.Term value, IEnumerable<IEnumerable<Binding>> val)
        {
            throw new NotImplementedException();
        }

        public void SendMessage(Ast.Message value)
        {
            if (value == null)
                throw new ArgumentException("Message is null");
            Ast.Message msg = value;
            DkalInfoEventArgs msgInfo = new DkalInfoEventArgs(value);

            if (OnInfonProcessed != null)
                OnInfonProcessed(this, msgInfo);
        }

        public void Warning(string value)
        {
            throw new NotImplementedException();
        }

        public void RequestFinished()
        {
            if (OnRequestProcessed != null)
                OnRequestProcessed(this,"message processed");
        }

        #endregion
    }
    /// <summary>
    /// PayLoad for Dkal Message
    /// </summary>
    public class DkalInfoEventArgs : EventArgs
    {
        Ast.Message _dkalMessage = null;
        public DkalInfoEventArgs(Ast.Message dkalmessage)
        {
            if (dkalmessage == null)
                throw new ArgumentNullException("dkalmessage");
            this._dkalMessage = dkalmessage;

        }
        public Ast.Message DkalMessage
        {
            get { return _dkalMessage; }
        }
    }
}
